use crate::physical_plan::PhysicalPlanExpr;
use crate::data_frame::{DataInstance, RealValue, ConfValue};
use crate::run_plan::{HandResult, HandResultFail, HandResultOk};
use crate::lazy_static::__Deref;

pub fn gt(args: &[Box<PhysicalPlanExpr>], data: &mut DataInstance) -> HandResult{
    if args.len() != 2{
        return HandResult::new_fail(format!("gt args len must eq 2"));
    }

    let left = &args[0];
    let right = &args[1];

    match left.deref(){
        PhysicalPlanExpr::ConfValue(left_value) => {
            match right.deref(){
                PhysicalPlanExpr::ConfValue(right_value) => {
                    return HandResult::new_fail(
                        format!("physical func gt left and right both ConfValue")
                    );
                },
                PhysicalPlanExpr::RealValue(right_value) => {
                    match right_value{
                        RealValue::U64(_) => {
                            let left_value = left_value.to_u64()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::F64(_) => {
                            let left_value = left_value.to_f64()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::I64(_) => {
                            let left_value = left_value.to_i64()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::Boolean(_) => {
                            let left_value = left_value.to_boolean()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::Range {left,right} => {
                            let left_value = left_value.to_range()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::Utf8(_) => {
                            let left_value = left_value.to_utf8()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::Null => {
                            let left_value = left_value.to_null()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::DateTime(_) => {
                            let left_value = left_value.to_datetime()?;
                            return left_value.value.gt(&right_value);
                        },
                        RealValue::Duration(_) => {
                            let left_value = left_value.to_duration()?;
                            return left_value.value.gt(&right_value);
                        },
                    }
                },
                PhysicalPlanExpr::ElementValue(element_value) => {
                    let right_value = data.get_value(element_value)?;
                    return left_value.gt(&right_value.value);
                },
                PhysicalPlanExpr::ElementListValue(_) => {
                    return HandResult::new_fail(format!("physical func gt right ElementListValue not support"));
                },
            }
        },
        PhysicalPlanExpr::RealValue(left_value) => {
            match right.deref(){
                PhysicalPlanExpr::ConfValue(right_value) => {
                    match left_value{
                        RealValue::U64(_) => {
                            let right_value = right_value.to_u64()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::F64(_) => {
                            let right_value = right_value.to_f64()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::I64(_) => {
                            let right_value = right_value.to_i64()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Boolean(_) => {
                            let right_value = right_value.to_boolean()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Range {left,right} => {
                            let right_value = right_value.to_range()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Utf8(_) => {
                            let right_value = right_value.to_utf8()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Null => {
                            let right_value = right_value.to_null()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::DateTime(_) => {
                            let right_value = right_value.to_datetime()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Duration(_) => {
                            let right_value = right_value.to_duration()?;
                            return left_value.gt(&right_value.value);
                        },
                    }
                },
                PhysicalPlanExpr::RealValue(right_value) => {
                    return left_value.gt(right_value);
                },
                PhysicalPlanExpr::ElementValue(element_value) => {
                    let right_value = data.get_value(element_value)?;
                    return left_value.gt(&right_value.value);
                },
                PhysicalPlanExpr::ElementListValue(_) => {
                    return HandResult::new_fail(
                        format!("physical func gt right ElementListValue not support")
                    );
                },
            }
        },
        PhysicalPlanExpr::ElementValue(left_value) => {
            let left_value = data.get_value(left_value)?.value;
            match right.deref(){
                PhysicalPlanExpr::ConfValue(right_value) => {
                    match left_value{
                        RealValue::U64(_) => {
                            let right_value = right_value.to_u64()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::F64(_) => {
                            let right_value = right_value.to_f64()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::I64(_) => {
                            let right_value = right_value.to_i64()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Boolean(_) => {
                            let right_value = right_value.to_boolean()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Range {left,right} => {
                            let right_value = right_value.to_range()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Utf8(_) => {
                            let right_value = right_value.to_utf8()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Null => {
                            let right_value = right_value.to_null()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::DateTime(_) => {
                            let right_value = right_value.to_datetime()?;
                            return left_value.gt(&right_value.value);
                        },
                        RealValue::Duration(_) => {
                            let right_value = right_value.to_duration()?;
                            return left_value.gt(&right_value.value);
                        },
                    }
                },
                PhysicalPlanExpr::RealValue(right_value) => {
                    return left_value.gt(right_value);
                },
                PhysicalPlanExpr::ElementValue(element_value) => {
                    let right_value = data.get_value(element_value)?.value;
                    return left_value.gt(&right_value);
                },
                PhysicalPlanExpr::ElementListValue(_) => {
                    return HandResult::new_fail(format!("physical func gt right ElementListValue not support"));
                },
            }
        },
        PhysicalPlanExpr::ElementListValue(_) => {
            return HandResult::new_fail(format!("physical func gt left ElementListValue not support"));
        },
    }
}

pub fn ge(args: &[Box<PhysicalPlanExpr>], data: &mut DataInstance) -> HandResult {
    if args.len() != 2{
        return HandResult::new_fail(format!("ge args len must eq 2"));
    }

    let left  = &args[0];
    let right = &args[1];

    match left.deref(){
        PhysicalPlanExpr::RealValue(left) => {
            match right.deref(){
                PhysicalPlanExpr::RealValue(right) => {
                    return left.ge(right);
                },
                PhysicalPlanExpr::ElementValue(right) => {
                    let right = &data.get_value(right)?.value;
                    return left.ge(right);
                },
                PhysicalPlanExpr::ConfValue(right) => {
                    return ConfValue::_ge(left, right);
                },
                PhysicalPlanExpr::ElementListValue(_) => {
                    return HandResult::new_fail(format!("ge right is ElementListValue"));
                },
            }
        },
        PhysicalPlanExpr::ElementValue(left) => {
            let left = &data.get_value(left)?.value;
            match right.deref(){
                PhysicalPlanExpr::RealValue(right) => {
                    return left.ge(right);
                },
                PhysicalPlanExpr::ElementValue(right) => {
                    let right = &data.get_value(right)?.value;
                    return left.ge(right);
                },
                PhysicalPlanExpr::ElementListValue(_) => {
                    return HandResult::new_fail(format!("ge right is ElementListValue"));
                },
                PhysicalPlanExpr::ConfValue(right) => {
                    return ConfValue::_ge(left,right);
                },
            }
        },
        PhysicalPlanExpr::ConfValue(left) => {
            match right.deref(){
                PhysicalPlanExpr::RealValue(right) => {
                    return left.ge(right);
                },
                PhysicalPlanExpr::ElementValue(right) => {
                    let right = &data.get_value(right)?.value;
                    return left.ge(right);
                },
                PhysicalPlanExpr::ElementListValue(_) => {
                    return HandResult::new_fail(format!("ge right is ElementListValue"));
                },
                PhysicalPlanExpr::ConfValue(right) => {
                    return HandResult::new_fail(format!("ge left and right both ConfValue"));
                },
            }
        },
        PhysicalPlanExpr::ElementListValue(_) => {
            return HandResult::new_fail(format!("ge left is ElementListValue"));
        },
    }
}

pub fn and(args: &[Box<PhysicalPlanExpr>], data: &mut DataInstance) -> HandResult{
    if args.len() != 2{
        return HandResult::new_fail(format!("and args len must eq 2"));
    }

    let left  = &args[0];
    let right = &args[1];

    match left.deref(){
        PhysicalPlanExpr::RealValue(left) => {
            if let RealValue::Boolean(left) = left{
                match right.deref(){
                    PhysicalPlanExpr::RealValue(right) => {
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right))
                        }else{
                            return HandResult::new_fail(format!("and right is RealValue, but not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementValue(right) => {
                        let right = &data.get_value(right)?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right));
                        }else{
                            return HandResult::new_fail(format!("and right is ElementValue, but RealValue is not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementListValue(_) => {
                        return HandResult::new_fail(format!("and right is ElementListValue"));
                    },
                    PhysicalPlanExpr::ConfValue(right) => {
                        let right = &right.to_boolean()?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right));
                        }else{
                            return HandResult::new_fail(format!("and right is ConfValue, but cannot trans to Boolean"));
                        }
                    },
                }
            }else{
                return HandResult::new_fail(format!("and left is RealValue, but not Boolean"));
            }
        },
        PhysicalPlanExpr::ElementValue(left) => {
            let left = &data.get_value(left)?.value;
            if let RealValue::Boolean(left) = left{
                match right.deref(){
                    PhysicalPlanExpr::RealValue(right) => {
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right));
                        }else{
                            return HandResult::new_fail(format!("and right is RealValue, but not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementValue(right) => {
                        let right = &data.get_value(right)?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right));
                        }else{
                            return HandResult::new_fail(format!("and right is ElementValue, but not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementListValue(right) => {
                        return HandResult::new_fail(format!("and right is ElementListValue"));
                    },
                    PhysicalPlanExpr::ConfValue(right) => {
                        let right = &right.to_boolean()?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right));
                        }else{
                            return HandResult::new_fail(format!("and right is ConfValue, but cannot trans to Boolean"));
                        }
                    },
                }
            }else{
                return HandResult::new_fail(format!("and left is ElementValue, but RealValue is not Boolean"));
            }
        },
        PhysicalPlanExpr::ElementListValue(_) => {
            return HandResult::new_fail(format!("and left is ElementListValue"));
        },
        PhysicalPlanExpr::ConfValue(left) => {
            match right.deref(){
                PhysicalPlanExpr::RealValue(right) => {
                    if let RealValue::Boolean(right) = right{
                        let left = &left.to_boolean()?.value;
                        if let RealValue::Boolean(left) = left{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right));
                        }else{
                            return HandResult::new_fail(format!("and left is ConfValue, trans to Boolean failed"));
                        }
                    }else{
                        return HandResult::new_fail(format!("and right is RealValue, but not Boolean"));
                    }
                },
                PhysicalPlanExpr::ElementValue(right) => {
                    let right = &data.get_value(right)?.value;
                    if let RealValue::Boolean(right) = right{
                        let left = &left.to_boolean()?.value;
                        if let RealValue::Boolean(left) = left{
                            return HandResult::new_ok(RealValue::Boolean(*left && *right));
                        }else{
                            return HandResult::new_fail(format!("and left is ConfValue, trans to Boolean failed"));
                        }
                    }else{
                        return HandResult::new_fail(format!("and right is ElementValue, but not Boolean"));
                    }
                },
                PhysicalPlanExpr::ElementListValue(right) => {
                    return HandResult::new_fail(format!("and right is ElementListValue"));
                },
                PhysicalPlanExpr::ConfValue(right) => {
                    return HandResult::new_fail(format!("and left and right both ConfValue"));
                },
            }
        },
    }
}

pub fn or(args: &[Box<PhysicalPlanExpr>], data: &mut DataInstance) -> HandResult{
    if args.len() != 2{
        return HandResult::new_fail(format!("or args len must eq 2"));
    }

    let left  = &args[0];
    let right = &args[1];

    match left.deref(){
        PhysicalPlanExpr::RealValue(left) => {
            if let RealValue::Boolean(left) = left{
                match right.deref(){
                    PhysicalPlanExpr::RealValue(right) => {
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right))
                        }else{
                            return HandResult::new_fail(format!("or right is RealValue, but not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementValue(right) => {
                        let right = &data.get_value(right)?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right));
                        }else{
                            return HandResult::new_fail(format!("or right is ElementValue, but RealValue is not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementListValue(_) => {
                        return HandResult::new_fail(format!("or right is ElementListValue"));
                    },
                    PhysicalPlanExpr::ConfValue(right) => {
                        let right = &right.to_boolean()?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right));
                        }else{
                            return HandResult::new_fail(format!("or right is ConfValue, but cannot trans to Boolean"));
                        }
                    },
                }
            }else{
                return HandResult::new_fail(format!("or left is RealValue, but not Boolean"));
            }
        },
        PhysicalPlanExpr::ElementValue(left) => {
            let left = &data.get_value(left)?.value;
            if let RealValue::Boolean(left) = left{
                match right.deref(){
                    PhysicalPlanExpr::RealValue(right) => {
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right));
                        }else{
                            return HandResult::new_fail(format!("or right is RealValue, but not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementValue(right) => {
                        let right = &data.get_value(right)?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right));
                        }else{
                            return HandResult::new_fail(format!("or right is ElementValue, but not Boolean"));
                        }
                    },
                    PhysicalPlanExpr::ElementListValue(right) => {
                        return HandResult::new_fail(format!("or right is ElementListValue"));
                    },
                    PhysicalPlanExpr::ConfValue(right) => {
                        let right = &right.to_boolean()?.value;
                        if let RealValue::Boolean(right) = right{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right));
                        }else{
                            return HandResult::new_fail(format!("or right is ConfValue, but cannot trans to Boolean"));
                        }
                    },
                }
            }else{
                return HandResult::new_fail(format!("or left is ElementValue, but RealValue is not Boolean"));
            }
        },
        PhysicalPlanExpr::ElementListValue(_) => {
            return HandResult::new_fail(format!("or left is ElementListValue"));
        },
        PhysicalPlanExpr::ConfValue(left) => {
            match right.deref(){
                PhysicalPlanExpr::RealValue(right) => {
                    if let RealValue::Boolean(right) = right{
                        let left = &left.to_boolean()?.value;
                        if let RealValue::Boolean(left) = left{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right));
                        }else{
                            return HandResult::new_fail(format!("or left is ConfValue, trans to Boolean failed"));
                        }
                    }else{
                        return HandResult::new_fail(format!("or right is RealValue, but not Boolean"));
                    }
                },
                PhysicalPlanExpr::ElementValue(right) => {
                    let right = &data.get_value(right)?.value;
                    if let RealValue::Boolean(right) = right{
                        let left = &left.to_boolean()?.value;
                        if let RealValue::Boolean(left) = left{
                            return HandResult::new_ok(RealValue::Boolean(*left || *right));
                        }else{
                            return HandResult::new_fail(format!("or left is ConfValue, trans to Boolean failed"));
                        }
                    }else{
                        return HandResult::new_fail(format!("or right is ElementValue, but not Boolean"));
                    }
                },
                PhysicalPlanExpr::ElementListValue(right) => {
                    return HandResult::new_fail(format!("or right is ElementListValue"));
                },
                PhysicalPlanExpr::ConfValue(right) => {
                    return HandResult::new_fail(format!("or left and right both ConfValue"));
                },
            }
        },
    }
}